/**
 * @fileOverview Graph插件之VML
 * @author <a href="mailto:lexloo@126.com">lexloo</a>
 */
Gear.kernel.DefPlugIn('plugIns/graph/vml.js', function() {
	var G = Gear.Graph;
	var doc = document;
	var concat = 'concat';
	var appendChild = "appendChild";
	var rp = 'replace';
	var proto = 'prototype';
	var Str = String;
	var split = "split";
	var E = '';
	var S = ' ';
	var apply = 'apply';
	var formatrg = /\{(\d+)\}/g;
	var length = 'length';
	var createNode;
	var coordsize = 1e3 + S + 1e3;
	var coordorigin = '0 0';
	var toStr = 'toString';
	var has = 'hasOwnProperty';
	var separator = /[, ]+/;
	var toFloat = parseFloat;
	var toInt = parseInt;
	var lowerCase = Str[proto].toLowerCase;
	var zoom = 10;
	var pathlike = {path:1, rect:1};
	var map = {M: "m", L: "l", C: "c", Z: "x", m: "t", l: "r", c: "v", z: "x"};
    var bites = /([clmz]),?([^clmz]*)/gi;
    var blurregexp = / progid:\S+Blur\([^\)]+\)/g;
    var val = /-?[^,\s-]+/g;
    
	G[toStr] = function() {
		return "浏览器不支持 SVG，使用VML。";
	};

	/* 内部计数器 */
	G._id_ = 1;
	
    var pathToAbsolute = $Lang.memoize(function (pathArray) {
        if (!R.is(pathArray, array) || !R.is(pathArray && pathArray[0], array)) { // rough assumption
            pathArray = R.parsePathString(pathArray);
        }
        var res = [],
            x = 0,
            y = 0,
            mx = 0,
            my = 0,
            start = 0;
        if (pathArray[0][0] == "M") {
            x = +pathArray[0][1];
            y = +pathArray[0][2];
            mx = x;
            my = y;
            start++;
            res[0] = ["M", x, y];
        }
        for (var i = start, ii = pathArray[length]; i < ii; i++) {
            var r = res[i] = [],
                pa = pathArray[i];
            if (pa[0] != upperCase.call(pa[0])) {
                r[0] = upperCase.call(pa[0]);
                switch (r[0]) {
                    case "A":
                        r[1] = pa[1];
                        r[2] = pa[2];
                        r[3] = pa[3];
                        r[4] = pa[4];
                        r[5] = pa[5];
                        r[6] = +(pa[6] + x);
                        r[7] = +(pa[7] + y);
                        break;
                    case "V":
                        r[1] = +pa[1] + y;
                        break;
                    case "H":
                        r[1] = +pa[1] + x;
                        break;
                    case "M":
                        mx = +pa[1] + x;
                        my = +pa[2] + y;
                    default:
                        for (var j = 1, jj = pa[length]; j < jj; j++) {
                            r[j] = +pa[j] + ((j % 2) ? x : y);
                        }
                }
            } else {
                for (var k = 0, kk = pa[length]; k < kk; k++) {
                    res[i][k] = pa[k];
                }
            }
            switch (r[0]) {
                case "Z":
                    x = mx;
                    y = my;
                    break;
                case "H":
                    x = r[1];
                    break;
                case "V":
                    y = r[1];
                    break;
                case "M":
                    mx = res[i][res[i][length] - 2];
                    my = res[i][res[i][length] - 1];
                default:
                    x = res[i][res[i][length] - 2];
                    y = res[i][res[i][length] - 1];
            }
        }
        res[toString] = R._path2string;
        return res;
    }, null, pathClone);

    path2vml = function (path) {
        var total =  /[ahqstv]/ig,
            command = pathToAbsolute;
        Str(path).match(total) && (command = path2curve);
        total = /[clmz]/g;
        if (command == pathToAbsolute && !Str(path).match(total)) {
            var res = Str(path)[rp](bites, function (all, command, args) {
                var vals = [],
                    isMove = lowerCase.call(command) == "m",
                    res = map[command];
                args[rp](val, function (value) {
                    if (isMove && vals[length] == 2) {
                        res += vals + map[command == "m" ? "l" : "L"];
                        vals = [];
                    }
                    vals[push](round(value * zoom));
                });
                return res + vals;
            });
            return res;
        }
        var pa = command(path), p, r;
        res = [];
        for (var i = 0, ii = pa[length]; i < ii; i++) {
            p = pa[i];
            r = lowerCase.call(pa[i][0]);
            r == "z" && (r = "x");
            for (var j = 1, jj = p[length]; j < jj; j++) {
                r += round(p[j] * zoom) + (j != jj - 1 ? "," : E);
            }
            res[push](r);
        }
        return res[join](S);
    };

thePath = function (pathString, vml) {
    var g = createNode("group");
    g.style.cssText = "position:absolute;left:0;top:0;width:" + vml.width + "px;height:" + vml.height + "px";
    g.coordsize = vml.coordsize;
    g.coordorigin = vml.coordorigin;
    var el = createNode("shape"), ol = el.style;
    ol.width = vml.width + "px";
    ol.height = vml.height + "px";
    el.coordsize = coordsize;
    el.coordorigin = vml.coordorigin;
    g[appendChild](el);
    var p = new Element(el, g, vml),
        attr = {fill: "none", stroke: "#000"};
    pathString && (attr.path = pathString);
    p.type = "path";
    p.path = [];
    p.Path = E;
    setFillAndStroke(p, attr);
    vml.canvas[appendChild](g);
    return p;
};
setFillAndStroke = function (o, params) {
    o.attrs = o.attrs || {};
    var node = o.node,
        a = o.attrs,
        s = node.style,
        xy,
        newpath = (params.x != a.x || params.y != a.y || params.width != a.width || params.height != a.height || params.r != a.r) && o.type == "rect",
        res = o;

    for (var par in params) if (params[has](par)) {
        a[par] = params[par];
    }
    if (newpath) {
        a.path = rectPath(a.x, a.y, a.width, a.height, a.r);
        o.X = a.x;
        o.Y = a.y;
        o.W = a.width;
        o.H = a.height;
    }
    params.href && (node.href = params.href);
    params.title && (node.title = params.title);
    params.target && (node.target = params.target);
    params.cursor && (s.cursor = params.cursor);
    "blur" in params && o.blur(params.blur);
    if (params.path && o.type == "path" || newpath) {
        node.path = path2vml(a.path);
    }
    if (params.rotation != null) {
        o.rotate(params.rotation, true);
    }
    if (params.translation) {
        xy = Str(params.translation)[split](separator);
        translate.call(o, xy[0], xy[1]);
        if (o._.rt.cx != null) {
            o._.rt.cx +=+ xy[0];
            o._.rt.cy +=+ xy[1];
            o.setBox(o.attrs, xy[0], xy[1]);
        }
    }
    if (params.scale) {
        xy = Str(params.scale)[split](separator);
        o.scale(+xy[0] || 1, +xy[1] || +xy[0] || 1, +xy[2] || null, +xy[3] || null);
    }
    if ("clip-rect" in params) {
        var rect = Str(params["clip-rect"])[split](separator);
        if (rect[length] == 4) {
            rect[2] = +rect[2] + (+rect[0]);
            rect[3] = +rect[3] + (+rect[1]);
            var div = node.clipRect || doc.createElement("div"),
                dstyle = div.style,
                group = node.parentNode;
            dstyle.clip = R.format("rect({1}px {2}px {3}px {0}px)", rect);
            if (!node.clipRect) {
                dstyle.position = "absolute";
                dstyle.top = 0;
                dstyle.left = 0;
                dstyle.width = o.paper.width + "px";
                dstyle.height = o.paper.height + "px";
                group.parentNode.insertBefore(div, group);
                div[appendChild](group);
                node.clipRect = div;
            }
        }
        if (!params["clip-rect"]) {
            node.clipRect && (node.clipRect.style.clip = E);
        }
    }
    if (o.type == "image" && params.src) {
        node.src = params.src;
    }
    if (o.type == "image" && params.opacity) {
        node.filterOpacity = ms + ".Alpha(opacity=" + (params.opacity * 100) + ")";
        s.filter = (node.filterMatrix || E) + (node.filterOpacity || E);
    }
    params.font && (s.font = params.font);
    params["font-family"] && (s.fontFamily = '"' + params["font-family"][split](",")[0][rp](/^['"]+|['"]+$/g, E) + '"');
    params["font-size"] && (s.fontSize = params["font-size"]);
    params["font-weight"] && (s.fontWeight = params["font-weight"]);
    params["font-style"] && (s.fontStyle = params["font-style"]);
    if (params.opacity != null || 
        params["stroke-width"] != null ||
        params.fill != null ||
        params.stroke != null ||
        params["stroke-width"] != null ||
        params["stroke-opacity"] != null ||
        params["fill-opacity"] != null ||
        params["stroke-dasharray"] != null ||
        params["stroke-miterlimit"] != null ||
        params["stroke-linejoin"] != null ||
        params["stroke-linecap"] != null) {
        node = o.shape || node;
        var fill = (node.getElementsByTagName(fillString) && node.getElementsByTagName(fillString)[0]),
            newfill = false;
        !fill && (newfill = fill = createNode(fillString));
        if ("fill-opacity" in params || "opacity" in params) {
            var opacity = ((+a["fill-opacity"] + 1 || 2) - 1) * ((+a.opacity + 1 || 2) - 1) * ((+R.getRGB(params.fill).o + 1 || 2) - 1);
            opacity = mmin(mmax(opacity, 0), 1);
            fill.opacity = opacity;
        }
        params.fill && (fill.on = true);
        if (fill.on == null || params.fill == "none") {
            fill.on = false;
        }
        if (fill.on && params.fill) {
            var isURL = params.fill.match(ISURL);
            if (isURL) {
                fill.src = isURL[1];
                fill.type = "tile";
            } else {
                fill.color = R.getRGB(params.fill).hex;
                fill.src = E;
                fill.type = "solid";
                if (R.getRGB(params.fill).error && (res.type in {circle: 1, ellipse: 1} || Str(params.fill).charAt() != "r") && addGradientFill(res, params.fill)) {
                    a.fill = "none";
                    a.gradient = params.fill;
                }
            }
        }
        newfill && node[appendChild](fill);
        var stroke = (node.getElementsByTagName("stroke") && node.getElementsByTagName("stroke")[0]),
        newstroke = false;
        !stroke && (newstroke = stroke = createNode("stroke"));
        if ((params.stroke && params.stroke != "none") ||
            params["stroke-width"] ||
            params["stroke-opacity"] != null ||
            params["stroke-dasharray"] ||
            params["stroke-miterlimit"] ||
            params["stroke-linejoin"] ||
            params["stroke-linecap"]) {
            stroke.on = true;
        }
        (params.stroke == "none" || stroke.on == null || params.stroke == 0 || params["stroke-width"] == 0) && (stroke.on = false);
        var strokeColor = R.getRGB(params.stroke);
        stroke.on && params.stroke && (stroke.color = strokeColor.hex);
        opacity = ((+a["stroke-opacity"] + 1 || 2) - 1) * ((+a.opacity + 1 || 2) - 1) * ((+strokeColor.o + 1 || 2) - 1);
        var width = (toFloat(params["stroke-width"]) || 1) * .75;
        opacity = mmin(mmax(opacity, 0), 1);
        params["stroke-width"] == null && (width = a["stroke-width"]);
        params["stroke-width"] && (stroke.weight = width);
        width && width < 1 && (opacity *= width) && (stroke.weight = 1);
        stroke.opacity = opacity;
        
        params["stroke-linejoin"] && (stroke.joinstyle = params["stroke-linejoin"] || "miter");
        stroke.miterlimit = params["stroke-miterlimit"] || 8;
        params["stroke-linecap"] && (stroke.endcap = params["stroke-linecap"] == "butt" ? "flat" : params["stroke-linecap"] == "square" ? "square" : "round");
        if (params["stroke-dasharray"]) {
            var dasharray = {
                "-": "shortdash",
                ".": "shortdot",
                "-.": "shortdashdot",
                "-..": "shortdashdotdot",
                ". ": "dot",
                "- ": "dash",
                "--": "longdash",
                "- .": "dashdot",
                "--.": "longdashdot",
                "--..": "longdashdotdot"
            };
            stroke.dashstyle = dasharray[has](params["stroke-dasharray"]) ? dasharray[params["stroke-dasharray"]] : E;
        }
        newstroke && node[appendChild](stroke);
    }
    if (res.type == "text") {
        s = res.paper.span.style;
        a.font && (s.font = a.font);
        a["font-family"] && (s.fontFamily = a["font-family"]);
        a["font-size"] && (s.fontSize = a["font-size"]);
        a["font-weight"] && (s.fontWeight = a["font-weight"]);
        a["font-style"] && (s.fontStyle = a["font-style"]);
        res.node.string && (res.paper.span.innerHTML = Str(res.node.string)[rp](/</g, "&#60;")[rp](/&/g, "&#38;")[rp](/\n/g, "<br>"));
        res.W = a.w = res.paper.span.offsetWidth;
        res.H = a.h = res.paper.span.offsetHeight;
        res.X = a.x;
        res.Y = a.y + round(res.H / 2);

        // text-anchor emulationm
        switch (a["text-anchor"]) {
            case "start":
                res.node.style["v-text-align"] = "left";
                res.bbx = round(res.W / 2);
            break;
            case "end":
                res.node.style["v-text-align"] = "right";
                res.bbx = -round(res.W / 2);
            break;
            default:
                res.node.style["v-text-align"] = "center";
            break;
        }
    }
};
addGradientFill = function (o, gradient) {
    o.attrs = o.attrs || {};
    var attrs = o.attrs,
        fill,
        type = "linear",
        fxfy = ".5 .5";
    o.attrs.gradient = gradient;
    gradient = Str(gradient)[rp](radial_gradient, function (all, fx, fy) {
        type = "radial";
        if (fx && fy) {
            fx = toFloat(fx);
            fy = toFloat(fy);
            pow(fx - .5, 2) + pow(fy - .5, 2) > .25 && (fy = math.sqrt(.25 - pow(fx - .5, 2)) * ((fy > .5) * 2 - 1) + .5);
            fxfy = fx + S + fy;
        }
        return E;
    });
    gradient = gradient[split](/\s*\-\s*/);
    if (type == "linear") {
        var angle = gradient.shift();
        angle = -toFloat(angle);
        if (isNaN(angle)) {
            return null;
        }
    }
    var dots = parseDots(gradient);
    if (!dots) {
        return null;
    }
    o = o.shape || o.node;
    fill = o.getElementsByTagName(fillString)[0] || createNode(fillString);
    !fill.parentNode && o.appendChild(fill);
    if (dots[length]) {
        fill.on = true;
        fill.method = "none";
        fill.color = dots[0].color;
        fill.color2 = dots[dots[length] - 1].color;
        var clrs = [];
        for (var i = 0, ii = dots[length]; i < ii; i++) {
            dots[i].offset && clrs[push](dots[i].offset + S + dots[i].color);
        }
        fill.colors && (fill.colors.value = clrs[length] ? clrs[join]() : "0% " + fill.color);
        if (type == "radial") {
            fill.type = "gradientradial";
            fill.focus = "100%";
            fill.focussize = fxfy;
            fill.focusposition = fxfy;
        } else {
            fill.type = "gradient";
            fill.angle = (270 - angle) % 360;
        }
    }
    return 1;
};

theCircle = function (vml, x, y, r) {
    var g = createNode("group"),
        o = createNode("oval"),
        ol = o.style;
    g.style.cssText = "position:absolute;left:0;top:0;width:" + vml.width + "px;height:" + vml.height + "px";
    g.coordsize = coordsize;
    g.coordorigin = vml.coordorigin;
    g[appendChild](o);
    var res = new Element(o, g, vml);
    res.type = "circle";
    setFillAndStroke(res, {stroke: "#000", fill: "none"});
    res.attrs.cx = x;
    res.attrs.cy = y;
    res.attrs.r = r;
    res.setBox({x: x - r, y: y - r, width: r * 2, height: r * 2});
    vml.canvas[appendChild](g);
    return res;
};
function rectPath(x, y, w, h, r) {
    if (r) {
        return R.format("M{0},{1}l{2},0a{3},{3},0,0,1,{3},{3}l0,{5}a{3},{3},0,0,1,{4},{3}l{6},0a{3},{3},0,0,1,{4},{4}l0,{7}a{3},{3},0,0,1,{3},{4}z", x + r, y, w - r * 2, r, -r, h - r * 2, r * 2 - w, r * 2 - h);
    } else {
        return R.format("M{0},{1}l{2},0,0,{3},{4},0z", x, y, w, h, -w);
    }
}
theRect = function (vml, x, y, w, h, r) {
    var path = rectPath(x, y, w, h, r),
        res = vml.path(path),
        a = res.attrs;
    res.X = a.x = x;
    res.Y = a.y = y;
    res.W = a.width = w;
    res.H = a.height = h;
    a.r = r;
    a.path = path;
    res.type = "rect";
    return res;
};
theEllipse = function (vml, x, y, rx, ry) {
    var g = createNode("group"),
        o = createNode("oval"),
        ol = o.style;
    g.style.cssText = "position:absolute;left:0;top:0;width:" + vml.width + "px;height:" + vml.height + "px";
    g.coordsize = coordsize;
    g.coordorigin = vml.coordorigin;
    g[appendChild](o);
    var res = new Element(o, g, vml);
    res.type = "ellipse";
    setFillAndStroke(res, {stroke: "#000"});
    res.attrs.cx = x;
    res.attrs.cy = y;
    res.attrs.rx = rx;
    res.attrs.ry = ry;
    res.setBox({x: x - rx, y: y - ry, width: rx * 2, height: ry * 2});
    vml.canvas[appendChild](g);
    return res;
};
theImage = function (vml, src, x, y, w, h) {
    var g = createNode("group"),
        o = createNode("image");
    g.style.cssText = "position:absolute;left:0;top:0;width:" + vml.width + "px;height:" + vml.height + "px";
    g.coordsize = coordsize;
    g.coordorigin = vml.coordorigin;
    o.src = src;
    g[appendChild](o);
    var res = new Element(o, g, vml);
    res.type = "image";
    res.attrs.src = src;
    res.attrs.x = x;
    res.attrs.y = y;
    res.attrs.w = w;
    res.attrs.h = h;
    res.setBox({x: x, y: y, width: w, height: h});
    vml.canvas[appendChild](g);
    return res;
};
theText = function (vml, x, y, text) {
    var g = createNode("group"),
        el = createNode("shape"),
        ol = el.style,
        path = createNode("path"),
        ps = path.style,
        o = createNode("textpath");
    g.style.cssText = "position:absolute;left:0;top:0;width:" + vml.width + "px;height:" + vml.height + "px";
    g.coordsize = coordsize;
    g.coordorigin = vml.coordorigin;
    path.v = R.format("m{0},{1}l{2},{1}", round(x * 10), round(y * 10), round(x * 10) + 1);
    path.textpathok = true;
    ol.width = vml.width;
    ol.height = vml.height;
    o.string = Str(text);
    o.on = true;
    el[appendChild](o);
    el[appendChild](path);
    g[appendChild](el);
    var res = new Element(o, g, vml);
    res.shape = el;
    res.textpath = path;
    res.type = "text";
    res.attrs.text = text;
    res.attrs.x = x;
    res.attrs.y = y;
    res.attrs.w = 1;
    res.attrs.h = 1;
    setFillAndStroke(res, {font: availableAttrs.font, stroke: "none", fill: "#000"});
    res.setBox();
    vml.canvas[appendChild](g);
    return res;
};
setSize = function (width, height) {
    var cs = this.canvas.style;
    width == +width && (width += "px");
    height == +height && (height += "px");
    cs.width = width;
    cs.height = height;
    cs.clip = "rect(0 " + width + " " + height + " 0)";
    return this;
};
var createNode;
doc.createStyleSheet().addRule(".rvml", "behavior:url(#default#VML)");
try {
    !doc.namespaces.rvml && doc.namespaces.add("rvml", "urn:schemas-microsoft-com:vml");
    createNode = function (tagName) {
        return doc.createElement('<rvml:' + tagName + ' class="rvml">');
    };
} catch (e) {
    createNode = function (tagName) {
        return doc.createElement('<' + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">');
    };
}
create = function () {
    var con = getContainer[apply](0, arguments),
        container = con.container,
        height = con.height,
        s,
        width = con.width,
        x = con.x,
        y = con.y;
    if (!container) {
        throw new Error("VML container not found.");
    }
    var res = new Paper,
        c = res.canvas = doc.createElement("div"),
        cs = c.style;
    x = x || 0;
    y = y || 0;
    width = width || 512;
    height = height || 342;
    width == +width && (width += "px");
    height == +height && (height += "px");
    res.width = 1e3;
    res.height = 1e3;
    res.coordsize = zoom * 1e3 + S + zoom * 1e3;
    res.coordorigin = "0 0";
    res.span = doc.createElement("span");
    res.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;";
    c[appendChild](res.span);
    cs.cssText = R.format("top:0;left:0;width:{0};height:{1};display:inline-block;position:relative;clip:rect(0 {0} {1} 0);overflow:hidden", width, height);
    if (container == 1) {
        doc.body[appendChild](c);
        cs.left = x + "px";
        cs.top = y + "px";
        cs.position = "absolute";
    } else {
        if (container.firstChild) {
            container.insertBefore(c, container.firstChild);
        } else {
            container[appendChild](c);
        }
    }
    plugins.call(res, res, R.fn);
    return res;
};
paperproto.clear = function () {
    this.canvas.innerHTML = E;
    this.span = doc.createElement("span");
    this.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;";
    this.canvas[appendChild](this.span);
    this.bottom = this.top = null;
};
paperproto.remove = function () {
    this.canvas.parentNode.removeChild(this.canvas);
    for (var i in this) {
        this[i] = removed(i);
    }
    return true;
};

	/* 元素定义 */
	var Element = function(node, group, vml) {
		var Rotation = 0, RotX = 0, RotY = 0, Scale = 1;
		this[0] = node;
		this.id = G._id_++;
		this.node = node;
		node.raphael = this;
		this.X = 0;
		this.Y = 0;
		this.attrs = {};
		this.Group = group;
		this.paper = vml;
		this._ = {
			tx : 0, ty : 0, rt : {
				deg : 0
			}, sx : 1, sy : 1
		};

		!vml.bottom && (vml.bottom = this);
		this.prev = vml.top;
		vml.top && (vml.top.next = this);
		vml.top = this;
		this.next = null;
	};

	elproto = Element[proto];
	elproto.rotate = function(deg, cx, cy) {
		if (this.removed) {
			return this;
		}

		if (deg == null) {
			if (this._.rt.cx) {
				return [ this._.rt.deg, this._.rt.cx, this._.rt.cy ][join](S);
			}

			return this._.rt.deg;
		}

		deg = Str(deg)[split](separator);
		if (deg[length] - 1) {
			cx = toFloat(deg[1]);
			cy = toFloat(deg[2]);
		}
		deg = toFloat(deg[0]);

		if (cx != null) {
			this._.rt.deg = deg;
		} else {
			this._.rt.deg += deg;
		}
		cy == null && (cx = null);
		this._.rt.cx = cx;
		this._.rt.cy = cy;
		this.setBox(this.attrs, cx, cy);
		this.Group.style.rotation = this._.rt.deg;

		return this;
	};

	elproto.setBox =
			function(params, cx, cy) {
				if (this.removed) {
					return this;
				}
				var gs = this.Group.style, os = (this.shape && this.shape.style) || this.node.style;
				params = params || {};
				for ( var i in params) {
					if (params[has](i)) {
						this.attrs[i] = params[i];
					}
				}
				cx = cx || this._.rt.cx;
				cy = cy || this._.rt.cy;
				var attr = this.attrs, x, y, w, h;
				switch (this.type) {
				case "circle":
					x = attr.cx - attr.r;
					y = attr.cy - attr.r;
					w = h = attr.r * 2;
					break;
				case "ellipse":
					x = attr.cx - attr.rx;
					y = attr.cy - attr.ry;
					w = attr.rx * 2;
					h = attr.ry * 2;
					break;
				case "image":
					x = +attr.x;
					y = +attr.y;
					w = attr.width || 0;
					h = attr.height || 0;
					break;
				case "text":
					this.textpath.v =
							[ "m", round(attr.x), ", ", round(attr.y - 2), "l", round(attr.x) + 1, ", ",
									round(attr.y - 2) ][join](E);
					x = attr.x - round(this.W / 2);
					y = attr.y - this.H / 2;
					w = this.W;
					h = this.H;
					break;
				case "rect":
				case "path":
					if (!this.attrs.path) {
						x = 0;
						y = 0;
						w = this.paper.width;
						h = this.paper.height;
					} else {
						var dim = pathDimensions(this.attrs.path);
						x = dim.x;
						y = dim.y;
						w = dim.width;
						h = dim.height;
					}
					break;
				default:
					x = 0;
					y = 0;
					w = this.paper.width;
					h = this.paper.height;
					break;
				}
				cx = (cx == null) ? x + w / 2 : cx;
				cy = (cy == null) ? y + h / 2 : cy;
				var left = cx - this.paper.width / 2, top = cy - this.paper.height / 2, t;
				gs.left != (t = left + "px") && (gs.left = t);
				gs.top != (t = top + "px") && (gs.top = t);
				this.X = pathlike[has](this.type) ? -left : x;
				this.Y = pathlike[has](this.type) ? -top : y;
				this.W = w;
				this.H = h;
				if (pathlike[has](this.type)) {
					os.left != (t = -left * zoom + "px") && (os.left = t);
					os.top != (t = -top * zoom + "px") && (os.top = t);
				} else if (this.type == "text") {
					os.left != (t = -left + "px") && (os.left = t);
					os.top != (t = -top + "px") && (os.top = t);
				} else {
					gs.width != (t = this.paper.width + "px") && (gs.width = t);
					gs.height != (t = this.paper.height + "px") && (gs.height = t);
					os.left != (t = x - left + "px") && (os.left = t);
					os.top != (t = y - top + "px") && (os.top = t);
					os.width != (t = w + "px") && (os.width = t);
					os.height != (t = h + "px") && (os.height = t);
				}
			};
	elproto.hide = function() {
		!this.removed && (this.Group.style.display = "none");

		return this;
	};
	elproto.show = function() {
		!this.removed && (this.Group.style.display = "block");

		return this;
	};

	elproto.getBBox = function() {
		if (this.removed) {
			return this;
		}
		if (pathlike[has](this.type)) {
			return pathDimensions(this.attrs.path);
		}
		return {
			x : this.X + (this.bbx || 0), y : this.Y, width : this.W, height : this.H
		};
	};

	elproto.remove = function() {
		if (this.removed) {
			return;
		}
		tear(this, this.paper);
		this.node.parentNode.removeChild(this.node);
		this.Group.parentNode.removeChild(this.Group);
		this.shape && this.shape.parentNode.removeChild(this.shape);
		for ( var i in this) {
			delete this[i];
		}
		this.removed = true;
	};

	elproto.attr =
			function(name, value) {
				if (this.removed) {
					return this;
				}
				if (name == null) {
					var res = {};
					for ( var i in this.attrs)
						if (this.attrs[has](i)) {
							res[i] = this.attrs[i];
						}
					this._.rt.deg && (res.rotation = this.rotate());
					(this._.sx != 1 || this._.sy != 1) && (res.scale = this.scale());
					res.gradient && res.fill == "none" && (res.fill = res.gradient) && delete res.gradient;
					return res;
				}
				if (value == null && R.is(name, "string")) {
					if (name == "translation") {
						return translate.call(this);
					}
					if (name == "rotation") {
						return this.rotate();
					}
					if (name == "scale") {
						return this.scale();
					}
					if (name == fillString && this.attrs.fill == "none" && this.attrs.gradient) {
						return this.attrs.gradient;
					}
					return this.attrs[name];
				}
				if (this.attrs && value == null && R.is(name, array)) {
					var ii, values = {};
					for (i = 0, ii = name[length]; i < ii; i++) {
						values[name[i]] = this.attr(name[i]);
					}
					return values;
				}
				var params;
				if (value != null) {
					params = {};
					params[name] = value;
				}
				value == null && R.is(name, "object") && (params = name);
				if (params) {
					for ( var key in this.paper.customAttributes)
						if (this.paper.customAttributes[has](key) && params[has](key)
								&& R.is(this.paper.customAttributes[key], "function")) {
							var par = this.paper.customAttributes[key].apply(this, [][concat](params[key]));
							this.attrs[key] = params[key];
							for ( var subkey in par)
								if (par[has](subkey)) {
									params[subkey] = par[subkey];
								}
						}
					if (params.text && this.type == "text") {
						this.node.string = params.text;
					}
					setFillAndStroke(this, params);
					if (params.gradient && (( {
						circle : 1, ellipse : 1
					})[has](this.type) || Str(params.gradient).charAt() != "r")) {
						addGradientFill(this, params.gradient);
					}
					(!pathlike[has](this.type) || this._.rt.deg) && this.setBox(this.attrs);
				}
				return this;
			};
	elproto.toFront = function() {
		!this.removed && this.Group.parentNode[appendChild](this.Group);
		this.paper.top != this && tofront(this, this.paper);
		return this;
	};
	elproto.toBack = function() {
		if (this.removed) {
			return this;
		}
		if (this.Group.parentNode.firstChild != this.Group) {
			this.Group.parentNode.insertBefore(this.Group, this.Group.parentNode.firstChild);
			toback(this, this.paper);
		}
		return this;
	};
	elproto.insertAfter = function(element) {
		if (this.removed) {
			return this;
		}
		if (element.constructor == Set) {
			element = element[element.length - 1];
		}
		if (element.Group.nextSibling) {
			element.Group.parentNode.insertBefore(this.Group, element.Group.nextSibling);
		} else {
			element.Group.parentNode[appendChild](this.Group);
		}
		insertafter(this, element, this.paper);
		return this;
	};
	elproto.insertBefore = function(element) {
		if (this.removed) {
			return this;
		}
		if (element.constructor == Set) {
			element = element[0];
		}
		element.Group.parentNode.insertBefore(this.Group, element.Group);
		insertbefore(this, element, this.paper);
		return this;
	};
	elproto.blur = function(size) {
		var s = this.node.runtimeStyle, f = s.filter;
		f = f.replace(blurregexp, E);
		if (+size !== 0) {
			this.attrs.blur = size;
			s.filter = f + S + ms + ".Blur(pixelradius=" + (+size || 1.5) + ")";
			s.margin = R.format("-{0}px 0 0 -{0}px", round(+size || 1.5));
		} else {
			s.filter = f;
			s.margin = 0;
			delete this.attrs.blur;
		}
	};
});